package org.zbclan.bf2.rcon;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.HttpCookie;
import java.net.Socket;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Pattern;

import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;

import org.apache.commons.io.FileUtils;
import org.apache.pivot.beans.BXMLSerializer;
import org.apache.pivot.collections.Map;
import org.apache.pivot.serialization.BinarySerializer;
import org.apache.pivot.serialization.SerializationException;
import org.apache.pivot.util.concurrent.Task;
import org.apache.pivot.util.concurrent.TaskListener;
import org.apache.pivot.web.PostQuery;
import org.apache.pivot.wtk.Application;
import org.apache.pivot.wtk.ApplicationContext;
import org.apache.pivot.wtk.BrowserApplicationContext;
import org.apache.pivot.wtk.DesktopApplicationContext;
import org.apache.pivot.wtk.Display;
import org.apache.pivot.wtk.TaskAdapter;
import org.apache.pivot.wtk.Window;

public class BF2RconApp implements Application, FileFilter {

	// private static final Logger logger = LoggerFactory
	// .getLogger(BF2RconApp.class);

	private static final Pattern PROFILE_DIR_PATTERN = Pattern
			.compile("\\d{4}");

	public static final ExecutorService CACHED_EXECUTOR = Executors
			.newCachedThreadPool(new DaemonThreadFactory("cached"));

	private Window window;

	private String host;
	private int port;
	private boolean secure;

	private String context;

	private ResourceBundle bundle;

	@Override
	public void startup(Display display, Map<String, String> properties)
			throws Exception {

		bundle = ResourceBundle.getBundle("message");

		try {
			nonUIStartup(display, properties);
		} catch (Exception ex) {
			/*
			 * log to dom
			 */
			echo(MessageFormat.format(bundle.getString("failure"),
					ex.getLocalizedMessage()));
		}

		return;
	}

	private void nonUIStartup(Display display, Map<String, String> properties)
			throws IOException, SerializationException, URISyntaxException {

		echo(bundle.getString("launching"));

		BXMLSerializer bxmlSerializer = new BXMLSerializer();
		window = (Window) bxmlSerializer.readObject(BF2RconApp.class,
				"hello.bxml");
		window.open(display);

		// URL origin;
		// if (properties.containsKey("origin")) {
		// /*
		// * for testing
		// */
		// origin = new URL(properties.get("origin"));
		// } else {
		// origin = ApplicationContext.getOrigin();
		// }
		URL origin = ApplicationContext.getOrigin();
		System.out.println("origin: " + origin.toString());

		host = origin.getHost();
		port = origin.getPort();
		secure = origin.getProtocol().equals("HTTPS");

		context = properties.get("context");
		String session = properties.get("session");

		HttpCookie cookie = new HttpCookie("JSESSIONID", session);
		cookie.setPath(context);

		CookieManager cookieManager = new CookieManager();
		cookieManager.getCookieStore().add(origin.toURI(), cookie);

		CookieHandler.setDefault(cookieManager);

		String cmd = properties.get("cmd");
		if (Command.HOME_BACKUP.equals(cmd)) {
			homeBackup();
			// } else if ("uploadLocalProfiles".equals(cmd)) {
			// uploadLocalProfiles(properties.get("dir").split(","));
		} else if (Command.SOUND_CAPTURE.equals(cmd)) {
			soundCapture();
		} else if (Command.SOUND_PLAYBACK.equals(cmd)) {
			soundPlayback();
		} else {
			echo(MessageFormat.format(bundle.getString("invalid.command"), cmd));
		}

		return;
	}

	// private void uploadLocalProfiles(String[] dir) {
	//
	// echo("scanning local profiles...");
	//
	// return;
	// }

	private void soundPlayback() {
		// TODO Auto-generated method stub

	}

	private void soundCapture() {

		// echo(bundle.getString("scanning"));
		echo("transmitting audio data: " + host);
		try {
			Socket socket = new Socket(host, 2010);

			/*
			 * sound capture
			 */
			final OutputStream out = socket.getOutputStream();
			SoundCapture soundCapture = new SoundCapture(
					new SoundCapture.Callback() {

						@Override
						public void captured(byte[] data, int size) {
							try {
								out.write(data, 0, size);
							} catch (IOException ex) {
								ex.printStackTrace();
								echo(ex.toString());
							}
						}
					});
			soundCapture.start(new LineListener() {

				@Override
				public void update(LineEvent event) {
					System.out.println(event.getLine().getClass() + ": "
							+ event.getType().toString());
				}
			});

			/*
			 * sound playback
			 */
			final InputStream in = socket.getInputStream();
			SoundPlayback soundPlayback = new SoundPlayback(
					new SoundPlayback.Callback() {

						@Override
						public int fetch(byte[] buffer) {
							int read;
							try {
								read = in.read(buffer);
							} catch (IOException ex) {
								ex.printStackTrace();
								read = -1;
							}
							return read;
						}
					});
			soundPlayback.start(new LineListener() {

				@Override
				public void update(LineEvent event) {
					System.out.println(event.getLine().getClass() + ": "
							+ event.getType().toString());
				}
			});

		} catch (Exception ex) {
			ex.printStackTrace();
			echo(ex.toString());
		}

		return;
	}

	private void homeBackup() throws IOException {

		echo(bundle.getString("scanning"));
		Home home = loadHome();

		echo(bundle.getString("uploading"));
		PostQuery q = new PostQuery(host, port, context
				+ "/query/local-profile", secure);
		q.setSerializer(new BinarySerializer());
		q.setValue(home);
		q.execute(new TaskAdapter<URL>(new TaskListener<URL>() {

			@Override
			public void taskExecuted(Task<URL> task) {
				echo("success. you can safely close the window.");
			}

			@Override
			public void executeFailed(Task<URL> task) {
				echo("failure. refresh page to try again.");
			}
		}));

		return;
	}

	private Home loadHome() throws IOException {

		Home home = new Home();
		home.setPlatformId(new HardwareQuery().getMotherboardSN());
		home.setPlatformUser(System.getProperty("user.name"));
		home.setPlatformOSName(System.getProperty("os.name"));
		home.setPlatformOSArch(System.getProperty("os.arch"));
		home.setPlatformOSVersion(System.getProperty("os.version"));

		RegQuery regQuery = new RegQuery();

		String homePath = regQuery
				.readValue(
						"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
						"Personal")
				+ "\\Battlefield 2";

		File profilesDir = new File(homePath, "Profiles");
		home.setGlobal(FileUtils.readFileToString(new File(profilesDir,
				"Global.con")));
		ArrayList<Profile> profiles = new ArrayList<Profile>();
		for (File d : profilesDir.listFiles(this)) {
			Profile p = new Profile();
			p.setDir(d.getName());
			p.setAudio(FileUtils.readFileToString(new File(d, "Audio.con")));
			p.setControls(FileUtils
					.readFileToString(new File(d, "Controls.con")));
			p.setGeneral(FileUtils.readFileToString(new File(d, "General.con")));
			p.setHaptic(FileUtils.readFileToString(new File(d, "Haptic.con")));
			p.setMapList(FileUtils.readFileToString(new File(d, "mapList.con")));
			p.setProfile(FileUtils.readFileToString(new File(d, "Profile.con")));
			p.setServerSettings(FileUtils.readFileToString(new File(d,
					"ServerSettings.con")));
			p.setVideo(FileUtils.readFileToString(new File(d, "Video.con")));
			profiles.add(p);
		}
		home.setProfiles(profiles);

		return home;
	}

	@Override
	public boolean shutdown(boolean optional) {
		if (window != null) {
			window.close();
		}

		return false;
	}

	@Override
	public void suspend() {
	}

	@Override
	public void resume() {
	}

	public void echo(String text) {
		BrowserApplicationContext.eval("echo('" + text + "')", this);
	}

	@Override
	public boolean accept(File pathname) {
		return pathname.isDirectory()
				&& PROFILE_DIR_PATTERN.matcher(pathname.getName()).matches();
	}

	public static void main(String[] args) {

		List<String> list = new ArrayList<String>(Arrays.asList(args));
		list.add("--origin=http://127.0.0.1:8080");
		list.add("--context=/bf2rcon");

		DesktopApplicationContext.main(BF2RconApp.class,
				list.toArray(new String[list.size()]));
	}
}
